package com.frank.oj.manager;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.frank.model.entity.contest.Contest;
import com.frank.model.entity.contest.ContestRegister;
import com.frank.oj.common.exception.StatusFailException;
import com.frank.oj.common.exception.StatusForbiddenException;
import com.frank.oj.constant.Constants;
import com.frank.oj.model.vo.AdminContestVO;
import com.frank.oj.model.vo.ContestAwardConfigVO;
import com.frank.oj.service.entity.contest.ContestEntityService;
import com.frank.oj.service.entity.contest.ContestRegisterEntityService;
import com.frank.oj.shiro.AccountProfile;
import com.frank.oj.validator.ContestValidator;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

import static com.frank.oj.manager.GroupContestManager.setStarAccount;

/**
 * @author frank
 * @Date 2024/4/7
 */
@Component
public class AdminContestManager {
    @Resource
    private ContestEntityService contestEntityService;
    @Resource
    private ContestValidator contestValidator;
    @Resource
    private ContestRegisterEntityService contestRegisterEntityService;

    public IPage<Contest> getContestList(Integer limit, Integer currentPage, String keyword) {

        if (currentPage == null || currentPage < 1) currentPage = 1;
        if (limit == null || limit < 1) limit = 10;
        IPage<Contest> page = new Page<>(currentPage, limit);

        LambdaQueryWrapper<Contest> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(Contest.class, tableFieldInfo -> !tableFieldInfo.getColumn().equals("pwd"));
        if (!StringUtils.isEmpty(keyword)) {
            keyword = keyword.trim();
            queryWrapper.like(Contest::getTitle, keyword).or()
                    .like(Contest::getId, keyword);
        }
        queryWrapper.eq(Contest::getIsGroup, false).orderByDesc(Contest::getStartTime);

        return contestEntityService.page(page, queryWrapper);
    }

    public void changeContestVisible(Long cid, String uid, Boolean visible) throws StatusForbiddenException, StatusFailException {

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");

        if (!isRoot && !userRole.getUid().equals(uid)) {
            throw new StatusForbiddenException("对不起，您无权限操作");
        }

        boolean isOk = contestEntityService.saveOrUpdate(new Contest().setId(cid).setVisible(visible));
        if (!isOk) {
            throw new StatusFailException("对不起，操作失败");
        }
    }

    public AdminContestVO getContest(Long cid) throws StatusFailException, StatusForbiddenException {
        Contest contest = contestEntityService.getById(cid);

        if (contest == null) {
            throw new StatusFailException("对不起，该比赛不存在");
        }

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");
        if (!isRoot && !userRole.getUid().equals(contest.getUid())) {
            throw new StatusForbiddenException("对不起，您无权限操作");
        }

        AdminContestVO adminContestVO = BeanUtil.copyProperties(contest, AdminContestVO.class, "starAccount");
        if (StringUtils.isEmpty(contest.getStarAccount())) {
            adminContestVO.setStarAccount(new ArrayList<>());
        } else {
            try {
                JSONObject jsonObject = JSONUtil.parseObj(contest.getStarAccount());
                List<String> starAccount = jsonObject.get("star_account", List.class);
                adminContestVO.setStarAccount(starAccount);
            } catch (ConvertException e) {
                adminContestVO.setStarAccount(new ArrayList<>());
            }
        }

        if (contest.getAwardType() != null && contest.getAwardType() != 0) {
            try {
                JSONObject jsonObject = JSONUtil.parseObj(contest.getAwardConfig());
                List<ContestAwardConfigVO> awardConfigList = jsonObject.get("config", List.class);
                adminContestVO.setAwardConfigList(awardConfigList);
            } catch (ConvertException e) {
                adminContestVO.setAwardConfigList(new ArrayList<>());
            }
        } else {
            adminContestVO.setAwardConfigList(new ArrayList<>());
        }

        return adminContestVO;
    }

    public void addContest(AdminContestVO adminContestVo) throws StatusFailException {
        contestValidator.validateContest(adminContestVo);

        Contest contest = BeanUtil
                .copyProperties(adminContestVo, Contest.class, "startAccount");
        JSONObject accountJson = new JSONObject();
        if (adminContestVo.getStarAccount() == null) {
            accountJson.set("star_account", new ArrayList<>());
        } else {
            accountJson.set("star_account", adminContestVo.getStarAccount());
        }
        contest.setStarAccount(accountJson.toString());

        if (adminContestVo.getAwardType() != null && adminContestVo.getAwardType() != 0) {
            JSONObject awardConfigJson = new JSONObject();
            List<ContestAwardConfigVO> awardConfigList = adminContestVo.getAwardConfigList();

            awardConfigList.sort(Comparator.comparingInt(ContestAwardConfigVO::getPriority));
            awardConfigJson.set("config", awardConfigList);
            contest.setAwardConfig(awardConfigJson.toString());
        }
        boolean saved = contestEntityService.save(contest);
        if (!saved) {
            throw new StatusFailException("添加失败");
        }
    }

    public void deleteContest(Long cid) throws StatusFailException {
        boolean removed = contestEntityService.removeById(cid);
        if (!removed) {
            throw new StatusFailException("删除失败");
        }
    }

    public void updateContest(AdminContestVO adminContestVo) throws StatusFailException, StatusForbiddenException {
        contestValidator.validateContest(adminContestVo);

        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();
        boolean isRoot = SecurityUtils.getSubject().hasRole("root");

        if (!isRoot && !userRole.getUid().equals(adminContestVo.getUid())) {
            throw new StatusForbiddenException("对不起，你无权限操作！");
        }

        Contest contest = setStarAccount(adminContestVo);

        Contest oldContest = contestEntityService.getById(contest.getId());
        boolean isOk = contestEntityService.saveOrUpdate(contest);
        if (isOk) { //修改密码后重置已注册的用户
            if (!contest.getAuth().equals(Constants.Contest.AUTH_PUBLIC.getCode())) {
                if (Objects.equals(oldContest.getPwd(), contest.getPwd())) {
                    LambdaQueryWrapper<ContestRegister> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(ContestRegister::getCid, contest.getId());
                    contestRegisterEntityService.remove(queryWrapper);
                }
            }
        } else {
            throw new StatusFailException("修改失败");
        }
    }

    public void cloneContest(Long cid) throws StatusFailException {
        Contest contest = contestEntityService.getById(cid);
        if (contest == null) {
            throw new StatusFailException("该比赛不存在");
        }
        AccountProfile userRole = (AccountProfile) SecurityUtils.getSubject().getPrincipal();

        contest.setUid(userRole.getUid())
                .setAuthor(userRole.getUsername())
                .setSource(cid.intValue())
                .setId(null)
                .setGmtCreate(null)
                .setGmtModified(null);
        contest.setTitle(contest.getTitle() + "[克隆赛]");
        contestEntityService.save(contest);
    }
}
